Passed
Push — master ( 0d18ca...6ca9b2 )
by Rafael S.
02:37
created

WaveFileReader   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 649
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 48
eloc 309
dl 0
loc 649
rs 8.5599
c 0
b 0
f 0

18 Functions

Rating   Name   Duplication   Size   Complexity  
B constructor 0 240 2
A readSmplChunk_ 0 28 3
A readUInt16 0 6 1
A readBextChunk_ 0 27 2
B readLISTSubChunks_ 0 31 5
A fromBuffer 0 19 2
A readDs64Chunk_ 0 26 3
A readFmtExtension_ 0 16 4
A bitDepthFromFmt_ 0 11 4
A readFmtChunk_ 0 18 2
A readCueChunk_ 0 20 3
A clearHeader 0 6 1
A readLISTChunk_ 0 19 4
A readJunkChunk_ 0 13 2
A readDataChunk_ 0 15 3
A readZSTR 0 9 3
A updateDataType 0 12 2
A readFactChunk_ 0 10 2

How to fix   Complexity   

Complexity

Complex classes like WaveFileReader often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*
2
 * Copyright (c) 2017-2019 Rafael da Silva Rocha.
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining
5
 * a copy of this software and associated documentation files (the
6
 * "Software"), to deal in the Software without restriction, including
7
 * without limitation the rights to use, copy, modify, merge, publish,
8
 * distribute, sublicense, and/or sell copies of the Software, and to
9
 * permit persons to whom the Software is furnished to do so, subject to
10
 * the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be
13
 * included in all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
 *
23
 */
24
25
/**
26
 * @fileoverview The WaveFileReader class.
27
 * @see https://github.com/rochars/wavefile
28
 */
29
30
/** @module wavefile */
31
32
import RIFFFile from './riff-file';
33
import writeString from './write-string';
34
import validateNumChannels from './validate-num-channels'; 
35
import validateSampleRate from './validate-sample-rate';
36
import {unpackString, unpack} from 'byte-data';
37
38
/**
39
 * A class to read wav files.
40
 */
41
export default class WaveFileReader extends RIFFFile {
42
43
  /**
44
   * @param {?Uint8Array=} wavBuffer A wave file buffer.
45
   * @throws {Error} If container is not RIFF, RIFX or RF64.
46
   * @throws {Error} If format is not WAVE.
47
   * @throws {Error} If no 'fmt ' chunk is found.
48
   * @throws {Error} If no 'data' chunk is found.
49
   */
50
  constructor(wavBuffer=null) {
51
    super();
52
    // Include 'RF64' as a supported container format
53
    this.supported_containers.push('RF64');
54
    /**
55
     * Audio formats.
56
     * Formats not listed here should be set to 65534,
57
     * the code for WAVE_FORMAT_EXTENSIBLE
58
     * @enum {number}
59
     * @protected
60
     */
61
    this.WAV_AUDIO_FORMATS = {
62
      '4': 17,
63
      '8': 1,
64
      '8a': 6,
65
      '8m': 7,
66
      '16': 1,
67
      '24': 1,
68
      '32': 1,
69
      '32f': 3,
70
      '64': 3
71
    };
72
    /**
73
     * The data of the 'fmt' chunk.
74
     * @type {!Object<string, *>}
75
     */
76
    this.fmt = {
77
      /** @type {string} */
78
      chunkId: '',
79
      /** @type {number} */
80
      chunkSize: 0,
81
      /** @type {number} */
82
      audioFormat: 0,
83
      /** @type {number} */
84
      numChannels: 0,
85
      /** @type {number} */
86
      sampleRate: 0,
87
      /** @type {number} */
88
      byteRate: 0,
89
      /** @type {number} */
90
      blockAlign: 0,
91
      /** @type {number} */
92
      bitsPerSample: 0,
93
      /** @type {number} */
94
      cbSize: 0,
95
      /** @type {number} */
96
      validBitsPerSample: 0,
97
      /** @type {number} */
98
      dwChannelMask: 0,
99
      /**
100
       * 4 32-bit values representing a 128-bit ID
101
       * @type {!Array<number>}
102
       */
103
      subformat: []
104
    };
105
    /**
106
     * The data of the 'fact' chunk.
107
     * @type {!Object<string, *>}
108
     */
109
    this.fact = {
110
      /** @type {string} */
111
      chunkId: '',
112
      /** @type {number} */
113
      chunkSize: 0,
114
      /** @type {number} */
115
      dwSampleLength: 0
116
    };
117
    /**
118
     * The data of the 'cue ' chunk.
119
     * @type {!Object<string, *>}
120
     */
121
    this.cue = {
122
      /** @type {string} */
123
      chunkId: '',
124
      /** @type {number} */
125
      chunkSize: 0,
126
      /** @type {number} */
127
      dwCuePoints: 0,
128
      /** @type {!Array<!Object>} */
129
      points: [],
130
    };
131
    /**
132
     * The data of the 'smpl' chunk.
133
     * @type {!Object<string, *>}
134
     */
135
    this.smpl = {
136
      /** @type {string} */
137
      chunkId: '',
138
      /** @type {number} */
139
      chunkSize: 0,
140
      /** @type {number} */
141
      dwManufacturer: 0,
142
      /** @type {number} */
143
      dwProduct: 0,
144
      /** @type {number} */
145
      dwSamplePeriod: 0,
146
      /** @type {number} */
147
      dwMIDIUnityNote: 0,
148
      /** @type {number} */
149
      dwMIDIPitchFraction: 0,
150
      /** @type {number} */
151
      dwSMPTEFormat: 0,
152
      /** @type {number} */
153
      dwSMPTEOffset: 0,
154
      /** @type {number} */
155
      dwNumSampleLoops: 0,
156
      /** @type {number} */
157
      dwSamplerData: 0,
158
      /** @type {!Array<!Object>} */
159
      loops: []
160
    };
161
    /**
162
     * The data of the 'bext' chunk.
163
     * @type {!Object<string, *>}
164
     */
165
    this.bext = {
166
      /** @type {string} */
167
      chunkId: '',
168
      /** @type {number} */
169
      chunkSize: 0,
170
      /** @type {string} */
171
      description: '', //256
172
      /** @type {string} */
173
      originator: '', //32
174
      /** @type {string} */
175
      originatorReference: '', //32
176
      /** @type {string} */
177
      originationDate: '', //10
178
      /** @type {string} */
179
      originationTime: '', //8
180
      /**
181
       * 2 32-bit values, timeReference high and low
182
       * @type {!Array<number>}
183
       */
184
      timeReference: [0, 0],
185
      /** @type {number} */
186
      version: 0, //WORD
187
      /** @type {string} */
188
      UMID: '', // 64 chars
189
      /** @type {number} */
190
      loudnessValue: 0, //WORD
191
      /** @type {number} */
192
      loudnessRange: 0, //WORD
193
      /** @type {number} */
194
      maxTruePeakLevel: 0, //WORD
195
      /** @type {number} */
196
      maxMomentaryLoudness: 0, //WORD
197
      /** @type {number} */
198
      maxShortTermLoudness: 0, //WORD
199
      /** @type {string} */
200
      reserved: '', //180
201
      /** @type {string} */
202
      codingHistory: '' // string, unlimited
203
    };
204
    /**
205
     * The data of the 'ds64' chunk.
206
     * Used only with RF64 files.
207
     * @type {!Object<string, *>}
208
     */
209
    this.ds64 = {
210
      /** @type {string} */
211
      chunkId: '',
212
      /** @type {number} */
213
      chunkSize: 0,
214
      /** @type {number} */
215
      riffSizeHigh: 0, // DWORD
216
      /** @type {number} */
217
      riffSizeLow: 0, // DWORD
218
      /** @type {number} */
219
      dataSizeHigh: 0, // DWORD
220
      /** @type {number} */
221
      dataSizeLow: 0, // DWORD
222
      /** @type {number} */
223
      originationTime: 0, // DWORD
224
      /** @type {number} */
225
      sampleCountHigh: 0, // DWORD
226
      /** @type {number} */
227
      sampleCountLow: 0 // DWORD
228
      /** @type {number} */
229
      //'tableLength': 0, // DWORD
230
      /** @type {!Array<number>} */
231
      //'table': []
232
    };
233
    /**
234
     * The data of the 'data' chunk.
235
     * @type {!Object<string, *>}
236
     */
237
    this.data = {
238
      /** @type {string} */
239
      chunkId: '',
240
      /** @type {number} */
241
      chunkSize: 0,
242
      /** @type {!Uint8Array} */
243
      samples: new Uint8Array(0)
244
    };
245
    /**
246
     * The data of the 'LIST' chunks.
247
     * Each item in this list look like this:
248
     *  {
249
     *      chunkId: '',
250
     *      chunkSize: 0,
251
     *      format: '',
252
     *      subChunks: []
253
     *   }
254
     * @type {!Array<!Object>}
255
     */
256
    this.LIST = [];
257
    /**
258
     * The data of the 'junk' chunk.
259
     * @type {!Object<string, *>}
260
     */
261
    this.junk = {
262
      /** @type {string} */
263
      chunkId: '',
264
      /** @type {number} */
265
      chunkSize: 0,
266
      /** @type {!Array<number>} */
267
      chunkData: []
268
    };
269
    /**
270
     * The bit depth code according to the samples.
271
     * @type {string}
272
     */
273
    this.bitDepth = '0';
274
    /**
275
     * @type {!Object}
276
     * @protected
277
     */
278
    this.dataType = {};
279
    /**
280
     * @type {!Object}
281
     * @protected
282
     */
283
    this.uInt16 = {bits: 16, be: false};
284
    // Load a file from the buffer if one was passed
285
    // when creating the object
286
    if (wavBuffer) {
287
      this.fromBuffer(wavBuffer);
288
    }
289
  }
290
291
  /**
292
   * Set up the WaveFileParser object from a byte buffer.
293
   * @param {!Uint8Array} wavBuffer The buffer.
294
   * @param {boolean=} samples True if the samples should be loaded.
295
   * @throws {Error} If container is not RIFF, RIFX or RF64.
296
   * @throws {Error} If format is not WAVE.
297
   * @throws {Error} If no 'fmt ' chunk is found.
298
   * @throws {Error} If no 'data' chunk is found.
299
   */
300
  fromBuffer(wavBuffer, samples=true) {
301
    this.clearHeader();
302
    this.setSignature(wavBuffer);
303
    this.uInt16.be = this.uInt32.be;
304
    if (this.format != 'WAVE') {
305
      throw Error('Could not find the "WAVE" format identifier');
306
    }
307
    this.readDs64Chunk_(wavBuffer);
308
    this.readFmtChunk_(wavBuffer);
309
    this.readFactChunk_(wavBuffer);
310
    this.readBextChunk_(wavBuffer);
311
    this.readCueChunk_(wavBuffer);
312
    this.readSmplChunk_(wavBuffer);
313
    this.readDataChunk_(wavBuffer, samples);
314
    this.readJunkChunk_(wavBuffer);
315
    this.readLISTChunk_(wavBuffer);
316
    this.bitDepthFromFmt_();
317
    this.updateDataType();
318
  }
319
320
  /**
321
   * Reset some attributes of the object.
322
   * @protected
323
   * @ignore
324
   */
325
  clearHeader() {
326
    this.fmt.cbSize = 0;
327
    this.fmt.validBitsPerSample = 0;
328
    this.fact.chunkId = '';
329
    this.ds64.chunkId = '';
330
  }
331
332
  /**
333
   * Update the type definition used to read and write the samples.
334
   * @protected
335
   */
336
  updateDataType() {
337
    this.dataType = {
338
      bits: ((parseInt(this.bitDepth, 10) - 1) | 7) + 1,
339
      fp: this.bitDepth == '32f' || this.bitDepth == '64',
340
      signed: this.bitDepth != '8',
341
      be: this.container == 'RIFX'
342
    };
343
    if (['4', '8a', '8m'].indexOf(this.bitDepth) > -1 ) {
344
      this.dataType.bits = 8;
345
      this.dataType.signed = false;
346
    }
347
  }
348
349
  /**
350
   * Set the string code of the bit depth based on the 'fmt ' chunk.
351
   * @private
352
   */
353
  bitDepthFromFmt_() {
354
    if (this.fmt.audioFormat === 3 && this.fmt.bitsPerSample === 32) {
355
      this.bitDepth = '32f';
356
    } else if (this.fmt.audioFormat === 6) {
357
      this.bitDepth = '8a';
358
    } else if (this.fmt.audioFormat === 7) {
359
      this.bitDepth = '8m';
360
    } else {
361
      this.bitDepth = this.fmt.bitsPerSample.toString();
362
    }
363
  }
364
365
  /**
366
   * Read the 'fmt ' chunk of a wave file.
367
   * @param {!Uint8Array} buffer The wav file buffer.
368
   * @throws {Error} If no 'fmt ' chunk is found.
369
   * @private
370
   */
371
  readFmtChunk_(buffer) {
372
    /** @type {?Object} */
373
    let chunk = this.findChunk('fmt ');
374
    if (chunk) {
375
      this.head = chunk.chunkData.start;
376
      this.fmt.chunkId = chunk.chunkId;
377
      this.fmt.chunkSize = chunk.chunkSize;
378
      this.fmt.audioFormat = this.readUInt16(buffer);
379
      this.fmt.numChannels = this.readUInt16(buffer);
380
      this.fmt.sampleRate = this.readUInt32(buffer);
381
      this.fmt.byteRate = this.readUInt32(buffer);
382
      this.fmt.blockAlign = this.readUInt16(buffer);
383
      this.fmt.bitsPerSample = this.readUInt16(buffer);
384
      this.readFmtExtension_(buffer);
385
    } else {
386
      throw Error('Could not find the "fmt " chunk');
387
    }
388
  }
389
390
  /**
391
   * Read the 'fmt ' chunk extension.
392
   * @param {!Uint8Array} buffer The wav file buffer.
393
   * @private
394
   */
395
  readFmtExtension_(buffer) {
396
    if (this.fmt.chunkSize > 16) {
397
      this.fmt.cbSize = this.readUInt16(buffer);
398
      if (this.fmt.chunkSize > 18) {
399
        this.fmt.validBitsPerSample = this.readUInt16(buffer);
400
        if (this.fmt.chunkSize > 20) {
401
          this.fmt.dwChannelMask = this.readUInt32(buffer);
402
          this.fmt.subformat = [
403
            this.readUInt32(buffer),
404
            this.readUInt32(buffer),
405
            this.readUInt32(buffer),
406
            this.readUInt32(buffer)];
407
        }
408
      }
409
    }
410
  }
411
412
  /**
413
   * Read the 'fact' chunk of a wav file.
414
   * @param {!Uint8Array} buffer The wav file buffer.
415
   * @private
416
   */
417
  readFactChunk_(buffer) {
418
    /** @type {?Object} */
419
    let chunk = this.findChunk('fact');
420
    if (chunk) {
421
      this.head = chunk.chunkData.start;
422
      this.fact.chunkId = chunk.chunkId;
423
      this.fact.chunkSize = chunk.chunkSize;
424
      this.fact.dwSampleLength = this.readUInt32(buffer);
425
    }
426
  }
427
428
  /**
429
   * Read the 'cue ' chunk of a wave file.
430
   * @param {!Uint8Array} buffer The wav file buffer.
431
   * @private
432
   */
433
  readCueChunk_(buffer) {
434
    /** @type {?Object} */
435
    let chunk = this.findChunk('cue ');
436
    if (chunk) {
437
      this.head = chunk.chunkData.start;
438
      this.cue.chunkId = chunk.chunkId;
439
      this.cue.chunkSize = chunk.chunkSize;
440
      this.cue.dwCuePoints = this.readUInt32(buffer);
441
      for (let i = 0; i < this.cue.dwCuePoints; i++) {
442
        this.cue.points.push({
443
          dwName: this.readUInt32(buffer),
444
          dwPosition: this.readUInt32(buffer),
445
          fccChunk: this.readString(buffer, 4),
446
          dwChunkStart: this.readUInt32(buffer),
447
          dwBlockStart: this.readUInt32(buffer),
448
          dwSampleOffset: this.readUInt32(buffer),
449
        });
450
      }
451
    }
452
  }
453
454
  /**
455
   * Read the 'smpl' chunk of a wave file.
456
   * @param {!Uint8Array} buffer The wav file buffer.
457
   * @private
458
   */
459
  readSmplChunk_(buffer) {
460
    /** @type {?Object} */
461
    let chunk = this.findChunk('smpl');
462
    if (chunk) {
463
      this.head = chunk.chunkData.start;
464
      this.smpl.chunkId = chunk.chunkId;
465
      this.smpl.chunkSize = chunk.chunkSize;
466
      this.smpl.dwManufacturer = this.readUInt32(buffer);
467
      this.smpl.dwProduct = this.readUInt32(buffer);
468
      this.smpl.dwSamplePeriod = this.readUInt32(buffer);
469
      this.smpl.dwMIDIUnityNote = this.readUInt32(buffer);
470
      this.smpl.dwMIDIPitchFraction = this.readUInt32(buffer);
471
      this.smpl.dwSMPTEFormat = this.readUInt32(buffer);
472
      this.smpl.dwSMPTEOffset = this.readUInt32(buffer);
473
      this.smpl.dwNumSampleLoops = this.readUInt32(buffer);
474
      this.smpl.dwSamplerData = this.readUInt32(buffer);
475
      for (let i = 0; i < this.smpl.dwNumSampleLoops; i++) {
476
        this.smpl.loops.push({
477
          dwName: this.readUInt32(buffer),
478
          dwType: this.readUInt32(buffer),
479
          dwStart: this.readUInt32(buffer),
480
          dwEnd: this.readUInt32(buffer),
481
          dwFraction: this.readUInt32(buffer),
482
          dwPlayCount: this.readUInt32(buffer),
483
        });
484
      }
485
    }
486
  }
487
488
  /**
489
   * Read the 'data' chunk of a wave file.
490
   * @param {!Uint8Array} buffer The wav file buffer.
491
   * @param {boolean} samples True if the samples should be loaded.
492
   * @throws {Error} If no 'data' chunk is found.
493
   * @private
494
   */
495
  readDataChunk_(buffer, samples) {
496
    /** @type {?Object} */
497
    let chunk = this.findChunk('data');
498
    if (chunk) {
499
      this.data.chunkId = 'data';
500
      this.data.chunkSize = chunk.chunkSize;
501
      if (samples) {
502
        this.data.samples = buffer.slice(
503
          chunk.chunkData.start,
504
          chunk.chunkData.end);
505
      }
506
    } else {
507
      throw Error('Could not find the "data" chunk');
508
    }
509
  }
510
511
  /**
512
   * Read the 'bext' chunk of a wav file.
513
   * @param {!Uint8Array} buffer The wav file buffer.
514
   * @private
515
   */
516
  readBextChunk_(buffer) {
517
    /** @type {?Object} */
518
    let chunk = this.findChunk('bext');
519
    if (chunk) {
520
      this.head = chunk.chunkData.start;
521
      this.bext.chunkId = chunk.chunkId;
522
      this.bext.chunkSize = chunk.chunkSize;
523
      this.bext.description = this.readString(buffer, 256);
524
      this.bext.originator = this.readString(buffer, 32);
525
      this.bext.originatorReference = this.readString(buffer, 32);
526
      this.bext.originationDate = this.readString(buffer, 10);
527
      this.bext.originationTime = this.readString(buffer, 8);
528
      this.bext.timeReference = [
529
        this.readUInt32(buffer),
530
        this.readUInt32(buffer)];
531
      this.bext.version = this.readUInt16(buffer);
532
      this.bext.UMID = this.readString(buffer, 64);
533
      this.bext.loudnessValue = this.readUInt16(buffer);
534
      this.bext.loudnessRange = this.readUInt16(buffer);
535
      this.bext.maxTruePeakLevel = this.readUInt16(buffer);
536
      this.bext.maxMomentaryLoudness = this.readUInt16(buffer);
537
      this.bext.maxShortTermLoudness = this.readUInt16(buffer);
538
      this.bext.reserved = this.readString(buffer, 180);
539
      this.bext.codingHistory = this.readString(
540
        buffer, this.bext.chunkSize - 602);
541
    }
542
  }
543
544
  /**
545
   * Read the 'ds64' chunk of a wave file.
546
   * @param {!Uint8Array} buffer The wav file buffer.
547
   * @throws {Error} If no 'ds64' chunk is found and the file is RF64.
548
   * @private
549
   */
550
  readDs64Chunk_(buffer) {
551
    /** @type {?Object} */
552
    let chunk = this.findChunk('ds64');
553
    if (chunk) {
554
      this.head = chunk.chunkData.start;
555
      this.ds64.chunkId = chunk.chunkId;
556
      this.ds64.chunkSize = chunk.chunkSize;
557
      this.ds64.riffSizeHigh = this.readUInt32(buffer);
558
      this.ds64.riffSizeLow = this.readUInt32(buffer);
559
      this.ds64.dataSizeHigh = this.readUInt32(buffer);
560
      this.ds64.dataSizeLow = this.readUInt32(buffer);
561
      this.ds64.originationTime = this.readUInt32(buffer);
562
      this.ds64.sampleCountHigh = this.readUInt32(buffer);
563
      this.ds64.sampleCountLow = this.readUInt32(buffer);
564
      //if (wav.ds64.chunkSize > 28) {
565
      //  wav.ds64.tableLength = unpack(
566
      //    chunkData.slice(28, 32), uInt32_);
567
      //  wav.ds64.table = chunkData.slice(
568
      //     32, 32 + wav.ds64.tableLength);
569
      //}
570
    } else {
571
      if (this.container == 'RF64') {
572
        throw Error('Could not find the "ds64" chunk');
573
      }
574
    }
575
  }
576
577
  /**
578
   * Read the 'LIST' chunks of a wave file.
579
   * @param {!Uint8Array} buffer The wav file buffer.
580
   * @private
581
   */
582
  readLISTChunk_(buffer) {
583
    /** @type {?Object} */
584
    let listChunks = this.findChunk('LIST', true);
585
    if (listChunks !== null) {
586
      for (let j=0; j < listChunks.length; j++) {
587
        /** @type {!Object} */
588
        let subChunk = listChunks[j];
589
        this.LIST.push({
590
          chunkId: subChunk.chunkId,
591
          chunkSize: subChunk.chunkSize,
592
          format: subChunk.format,
593
          subChunks: []});
594
        for (let x=0; x<subChunk.subChunks.length; x++) {
595
          this.readLISTSubChunks_(subChunk.subChunks[x],
596
            subChunk.format, buffer);
597
        }
598
      }
599
    }
600
  }
601
602
  /**
603
   * Read the sub chunks of a 'LIST' chunk.
604
   * @param {!Object} subChunk The 'LIST' subchunks.
605
   * @param {string} format The 'LIST' format, 'adtl' or 'INFO'.
606
   * @param {!Uint8Array} buffer The wav file buffer.
607
   * @private
608
   */
609
  readLISTSubChunks_(subChunk, format, buffer) {
610
    if (format == 'adtl') {
611
      if (['labl', 'note','ltxt'].indexOf(subChunk.chunkId) > -1) {
612
        this.head = subChunk.chunkData.start;
613
        /** @type {!Object<string, string|number>} */
614
        let item = {
615
          chunkId: subChunk.chunkId,
616
          chunkSize: subChunk.chunkSize,
617
          dwName: this.readUInt32(buffer)
618
        };
619
        if (subChunk.chunkId == 'ltxt') {
620
          item.dwSampleLength = this.readUInt32(buffer);
621
          item.dwPurposeID = this.readUInt32(buffer);
622
          item.dwCountry = this.readUInt16(buffer);
623
          item.dwLanguage = this.readUInt16(buffer);
624
          item.dwDialect = this.readUInt16(buffer);
625
          item.dwCodePage = this.readUInt16(buffer);
626
        }
627
        item.value = this.readZSTR(buffer, this.head);
628
        this.LIST[this.LIST.length - 1].subChunks.push(item);
629
      }
630
    // RIFF INFO tags like ICRD, ISFT, ICMT
631
    } else if(format == 'INFO') {
632
      this.head = subChunk.chunkData.start;
633
      this.LIST[this.LIST.length - 1].subChunks.push({
634
        chunkId: subChunk.chunkId,
635
        chunkSize: subChunk.chunkSize,
636
        value: this.readZSTR(buffer, this.head)
637
      });
638
    }
639
  }
640
641
  /**
642
   * Read the 'junk' chunk of a wave file.
643
   * @param {!Uint8Array} buffer The wav file buffer.
644
   * @private
645
   */
646
  readJunkChunk_(buffer) {
647
    /** @type {?Object} */
648
    let chunk = this.findChunk('junk');
649
    if (chunk) {
650
      this.junk = {
651
        chunkId: chunk.chunkId,
652
        chunkSize: chunk.chunkSize,
653
        chunkData: [].slice.call(buffer.slice(
654
          chunk.chunkData.start,
655
          chunk.chunkData.end))
656
      };
657
    }
658
  }
659
660
  /**
661
   * Read bytes as a ZSTR string.
662
   * @param {!Uint8Array} bytes The bytes.
663
   * @param {number} index the index to start reading.
664
   * @return {string} The string.
665
   * @protected
666
   */
667
  readZSTR(bytes, index=0) {
668
    for (let i = index; i < bytes.length; i++) {
669
      this.head++;
670
      if (bytes[i] === 0) {
671
        break;
672
      }
673
    }
674
    return unpackString(bytes, index, this.head - 1);
675
  }
676
677
  /**
678
   * Read a number from a chunk.
679
   * @param {!Uint8Array} bytes The chunk bytes.
680
   * @return {number} The number.
681
   * @protected
682
   */
683
  readUInt16(bytes) {
684
    /** @type {number} */
685
    let value = unpack(bytes, this.uInt16, this.head);
686
    this.head += 2;
687
    return value;
688
  }
689
}
690